TRATAMENTO DE DADOS NO PYTHON - PARTE 2

Autor

João Ricardo F. de Lima

Data de Publicação

29 de maio de 2024

Formatos Long e Wide

1 Tabelas de dados podem ser organizadas de diversas maneiras, conforme a necessidade, contexto ou, até mesmo, as preferências individuais do usuário. No entanto, para padronizar a organização de dados em tabelas e evitar situações caóticas, existem alguns princípios que devem ser seguidos para facilitar a interpretação e a manipulação de dados tabulares:

  1. Cada variável tem sua própria coluna
  2. Cada observação tem sua própria linha
  3. Cada valor tem sua própria célula

Existem dois tipos de transformações (opostas) em tabelas, que são úteis para organizá-las de modo a seguir (ou não) os 3 princípios destacados:

  • pivot(): faz a tabela ser mais “longa” ao aumentar o nº de linhas e diminuir o nº de colunas.
  • melt(): faz a tabela ser mais “larga” ao diminuir o nº de linhas e aumentar o nº de colunas.

Exemplo de transformação long:

Exemplo de transformação wide:

Dados de exemplo

# Importar bibliotecas
import pandas as pd
import janitor

# Dados do IPCA (Sidra/IBGE)
dados_sidra = pd.read_json(
    path_or_buf = "https://apisidra.ibge.gov.br/values/t/7060/n1/all/v/all/p/all/c315/7169/d/v63%202,v66%204,v69%202,v2265%202?formato=json"
    )

# Dados de expectativas de inflação (Focus/BCB)
dados_focus = pd.read_csv(
  filepath_or_buffer = "https://olinda.bcb.gov.br/olinda/servico/Expectativas/versao/v1/odata/ExpectativasMercadoAnuais?%24filter=%28Indicador%20eq%20%27IPCA%27%20or%20Indicador%20eq%20%27IGP-M%27%29%20and%20Data%20ge%20%272021-01-01%27%20and%20Data%20le%20%272024-04-12%27&%24format=text/csv&%24orderby=Data%20desc",
  decimal = ","
  )

Para exemplificar essas transformações, tem-se a tabela de dados do IPCA, extraida do Sidra/IBGE, que vem com as variáveis (var. mensal, acumulada, peso, etc.) empilhadas em uma única coluna, quebrando o primeiro princípio:

tabela = (
    dados_sidra
    .rename(columns = {"V": "valor", "D3C": "mes", "D2N": "variavel"})
    .query("valor != 'Valor'")
    .filter(items = ["mes", "variavel", "valor"])
)
tabela
        mes                variavel     valor
1    202001  IPCA - Variação mensal      0.21
2    202002  IPCA - Variação mensal      0.25
3    202003  IPCA - Variação mensal      0.07
4    202004  IPCA - Variação mensal     -0.31
5    202005  IPCA - Variação mensal     -0.38
..      ...                     ...       ...
204  202312      IPCA - Peso mensal  100.0000
205  202401      IPCA - Peso mensal  100.0000
206  202402      IPCA - Peso mensal  100.0000
207  202403      IPCA - Peso mensal  100.0000
208  202404      IPCA - Peso mensal  100.0000

[208 rows x 3 columns]

Para organizar essa tabela, conforme os 3 princípios, deve-se aplicar uma transformação de modo que resulte em uma coluna para cada uma das 4 variáveis que estão, atualmente, empilhadas. Ou seja, o objetivo é diminuir o nº de linhas e aumentar o nº de colunas. Isso pode ser feito com a função pivot():

# Formato wide: para quando é necessário colocar cada variável em uma coluna
tabela.pivot(
  index = "mes",         # coluna que unicamente identifica as observações
  columns = "variavel",  # coluna onde os nomes das variáveis estão
  values = "valor"       # coluna onde os valores das células estão
  )
variavel IPCA - Peso mensal  ... IPCA - Variação mensal
mes                          ...                       
202001             100.0000  ...                   0.21
202002             100.0000  ...                   0.25
202003             100.0000  ...                   0.07
202004             100.0000  ...                  -0.31
202005             100.0000  ...                  -0.38
202006             100.0000  ...                   0.26
202007             100.0000  ...                   0.36
202008             100.0000  ...                   0.24
202009             100.0000  ...                   0.64
202010             100.0000  ...                   0.86
202011             100.0000  ...                   0.89
202012             100.0000  ...                   1.35
202101             100.0000  ...                   0.25
202102             100.0000  ...                   0.86
202103             100.0000  ...                   0.93
202104             100.0000  ...                   0.31
202105             100.0000  ...                   0.83
202106             100.0000  ...                   0.53
202107             100.0000  ...                   0.96
202108             100.0000  ...                   0.87
202109             100.0000  ...                   1.16
202110             100.0000  ...                   1.25
202111             100.0000  ...                   0.95
202112             100.0000  ...                   0.73
202201             100.0000  ...                   0.54
202202             100.0000  ...                   1.01
202203             100.0000  ...                   1.62
202204             100.0000  ...                   1.06
202205             100.0000  ...                   0.47
202206             100.0000  ...                   0.67
202207             100.0000  ...                  -0.68
202208             100.0000  ...                  -0.36
202209             100.0000  ...                  -0.29
202210             100.0000  ...                   0.59
202211             100.0000  ...                   0.41
202212             100.0000  ...                   0.62
202301             100.0000  ...                   0.53
202302             100.0000  ...                   0.84
202303             100.0000  ...                   0.71
202304             100.0000  ...                   0.61
202305             100.0000  ...                   0.23
202306             100.0000  ...                  -0.08
202307             100.0000  ...                   0.12
202308             100.0000  ...                   0.23
202309             100.0000  ...                   0.26
202310             100.0000  ...                   0.24
202311             100.0000  ...                   0.28
202312             100.0000  ...                   0.56
202401             100.0000  ...                   0.42
202402             100.0000  ...                   0.83
202403             100.0000  ...                   0.16
202404             100.0000  ...                   0.38

[52 rows x 4 columns]

Um exemplo inverso pode ser visto com os dados de expectativas do Focus. Após alguns tratamentos, muda-se a organização da tabela para simular o problema de ter mais de uma observação nas linhas (isso é menos frequente, mas acontece). Observe:

# Dados de exemplo 2: expectativas Focus para o IPCA
dados = (
    dados_focus
    .query("Indicador == 'IPCA' and DataReferencia in [2024, 2025] and baseCalculo == 0")
    .filter(items = ["Data", "DataReferencia", "Mediana"], axis = "columns")
    .pivot(index = "Data", columns = "DataReferencia", values = "Mediana")
    .reset_index()
)
dados
DataReferencia        Data    2024    2025
0               2021-01-04  3.2000     NaN
1               2021-01-05  3.2000     NaN
2               2021-01-06  3.2000     NaN
3               2021-01-07  3.2000     NaN
4               2021-01-08  3.2400     NaN
..                     ...     ...     ...
817             2024-04-08  3.7510  3.5256
818             2024-04-09  3.7510  3.5287
819             2024-04-10  3.7000  3.5287
820             2024-04-11  3.7000  3.5287
821             2024-04-12  3.7093  3.5561

[822 rows x 3 columns]
dados
DataReferencia        Data    2024    2025
0               2021-01-04  3.2000     NaN
1               2021-01-05  3.2000     NaN
2               2021-01-06  3.2000     NaN
3               2021-01-07  3.2000     NaN
4               2021-01-08  3.2400     NaN
..                     ...     ...     ...
817             2024-04-08  3.7510  3.5256
818             2024-04-09  3.7510  3.5287
819             2024-04-10  3.7000  3.5287
820             2024-04-11  3.7000  3.5287
821             2024-04-12  3.7093  3.5561

[822 rows x 3 columns]

Note que os nomes de colunas 2024 e 2025 são, na verdade, valores de uma variável que se refere ao horizonte de expectativas (DataReferencia); e os valores nestas colunas representam os valores da variável Mediana, uma das estatísticas do Focus.

Assim, tem-se o problema de mais de uma observação por linha, de modo que para organizar a tabela, deve-se aplicar uma transformação que diminua o nº de colunas e aumente o nº de linhas. A função melt() faz isso:

# Formato long: para quando cada linha representa mais de uma observação
dados.melt(
  id_vars = ["Data"], # colunas que identificam as observações
  value_vars = [2024, 2025],       # colunas para transformar pro formato longo
  var_name = "data_ref",           # nome da coluna que armazenará os nomes de [2024, 2025]
  value_name = "mediana"           # nome da coluna que armazenará os valores de [2024, 2025]
  )
            Data data_ref  mediana
0     2021-01-04     2024   3.2000
1     2021-01-05     2024   3.2000
2     2021-01-06     2024   3.2000
3     2021-01-07     2024   3.2000
4     2021-01-08     2024   3.2400
...          ...      ...      ...
1639  2024-04-08     2025   3.5256
1640  2024-04-09     2025   3.5287
1641  2024-04-10     2025   3.5287
1642  2024-04-11     2025   3.5287
1643  2024-04-12     2025   3.5561

[1644 rows x 3 columns]

Cruzamento de Dados

Um cruzamento é uma maneira de conectar cada linha em X a zero, uma ou mais linhas em Y. O diagrama a seguir mostra as correspondências pelas “chaves” indicadas como pontos. O número de pontos é o número de correspondências e, ao mesmo tempo, é o número de linhas na tabela resultante.

Este é o tipo mais simples de cruzamento, chamado de inner join. Esse cruzamento retorna pares de observações somente para quando as chaves das tabelas corresponderem. Ou seja, as linhas sem correspondência não são incluídas no resultado final.

Um inner join mantém as observações que aparecem em ambas as tabelas. Um outer join mantém as observações que aparecem em pelo menos uma das tabelas. Existem três tipos de outer joins:

  • left join mantém todas as observações em x.
  • right join mantém todas as observações em y.
  • full join mantém todas as observações em x e y.

De forma ilustrada, estes cruzamentos podem ser representados assim:

Dados de exemplo

# Importar bibliotecas
import pandas as pd
import ipeadatapy as ipea
from bcb import sgs

# Dados do saldo do CAGED (Ipeadata)
dados_ipeadata = ipea.timeseries("CAGED12_SALDON12")

# Dados do IDP/BP - acum. 12m - US$ (milhões) (SGS/BCB)
dados_sgs = sgs.get(codes = {"idp": 24422}, 
  start = "2019-11-01", 
  end = "2024-03-01")

Cruzamento inner join

Para verificar como o inner join funciona, primeiro cria-se duas tabelas para as séries temporais do CAGED e do IDP, ou seja, a “chave” será a coluna data:

# Dados de exemplo:
tabela_caged = (
  dados_ipeadata
  .reset_index()
  .rename(columns = {"DATE": "data", "VALUE (Pessoa)": "caged"})
#  .filter(items = ["data", "caged"], axis = "columns").query("data <= '2020-06-01'")
  .set_index("data")
)
tabela_caged
                        CODE                   RAW DATE  ...  YEAR     caged
data                                                     ...                
2020-01-01  CAGED12_SALDON12  2020-01-01T00:00:00-03:00  ...  2020   92678.0
2020-02-01  CAGED12_SALDON12  2020-02-01T00:00:00-03:00  ...  2020  198368.0
2020-03-01  CAGED12_SALDON12  2020-03-01T00:00:00-03:00  ...  2020 -232316.0
2020-04-01  CAGED12_SALDON12  2020-04-01T00:00:00-03:00  ...  2020 -902317.0
2020-05-01  CAGED12_SALDON12  2020-05-01T00:00:00-03:00  ...  2020 -352790.0
2020-06-01  CAGED12_SALDON12  2020-06-01T00:00:00-03:00  ...  2020  -23111.0
2020-07-01  CAGED12_SALDON12  2020-07-01T00:00:00-03:00  ...  2020  123297.0
2020-08-01  CAGED12_SALDON12  2020-08-01T00:00:00-03:00  ...  2020  238125.0
2020-09-01  CAGED12_SALDON12  2020-09-01T00:00:00-03:00  ...  2020  313564.0
2020-10-01  CAGED12_SALDON12  2020-10-01T00:00:00-03:00  ...  2020  388938.0
2020-11-01  CAGED12_SALDON12  2020-11-01T00:00:00-03:00  ...  2020  408948.0
2020-12-01  CAGED12_SALDON12  2020-12-01T00:00:00-03:00  ...  2020  -82118.0
2021-01-01  CAGED12_SALDON12  2021-01-01T00:00:00-03:00  ...  2021  257059.0
2021-02-01  CAGED12_SALDON12  2021-02-01T00:00:00-03:00  ...  2021  397915.0
2021-03-01  CAGED12_SALDON12  2021-03-01T00:00:00-03:00  ...  2021  176330.0
2021-04-01  CAGED12_SALDON12  2021-04-01T00:00:00-03:00  ...  2021  113922.0
2021-05-01  CAGED12_SALDON12  2021-05-01T00:00:00-03:00  ...  2021  278705.0
2021-06-01  CAGED12_SALDON12  2021-06-01T00:00:00-03:00  ...  2021  310036.0
2021-07-01  CAGED12_SALDON12  2021-07-01T00:00:00-03:00  ...  2021  316725.0
2021-08-01  CAGED12_SALDON12  2021-08-01T00:00:00-03:00  ...  2021  373344.0
2021-09-01  CAGED12_SALDON12  2021-09-01T00:00:00-03:00  ...  2021  316993.0
2021-10-01  CAGED12_SALDON12  2021-10-01T00:00:00-03:00  ...  2021  253083.0
2021-11-01  CAGED12_SALDON12  2021-11-01T00:00:00-03:00  ...  2021  324112.0
2021-12-01  CAGED12_SALDON12  2021-12-01T00:00:00-03:00  ...  2021 -265811.0
2022-01-01  CAGED12_SALDON12  2022-01-01T00:00:00-03:00  ...  2022  155178.0
2022-02-01  CAGED12_SALDON12  2022-02-01T00:00:00-03:00  ...  2022  328507.0
2022-03-01  CAGED12_SALDON12  2022-03-01T00:00:00-03:00  ...  2022  136189.0
2022-04-01  CAGED12_SALDON12  2022-04-01T00:00:00-03:00  ...  2022  196966.0
2022-05-01  CAGED12_SALDON12  2022-05-01T00:00:00-03:00  ...  2022  277018.0
2022-06-01  CAGED12_SALDON12  2022-06-01T00:00:00-03:00  ...  2022  277944.0
2022-07-01  CAGED12_SALDON12  2022-07-01T00:00:00-03:00  ...  2022  218902.0
2022-08-01  CAGED12_SALDON12  2022-08-01T00:00:00-03:00  ...  2022  278639.0
2022-09-01  CAGED12_SALDON12  2022-09-01T00:00:00-03:00  ...  2022  278085.0
2022-10-01  CAGED12_SALDON12  2022-10-01T00:00:00-03:00  ...  2022  159454.0
2022-11-01  CAGED12_SALDON12  2022-11-01T00:00:00-03:00  ...  2022  135495.0
2022-12-01  CAGED12_SALDON12  2022-12-01T00:00:00-03:00  ...  2022 -431011.0
2023-01-01  CAGED12_SALDON12  2023-01-01T00:00:00-03:00  ...  2023   83297.0
2023-02-01  CAGED12_SALDON12  2023-02-01T00:00:00-03:00  ...  2023  241785.0
2023-03-01  CAGED12_SALDON12  2023-03-01T00:00:00-03:00  ...  2023  195171.0
2023-04-01  CAGED12_SALDON12  2023-04-01T00:00:00-03:00  ...  2023  180005.0
2023-05-01  CAGED12_SALDON12  2023-05-01T00:00:00-03:00  ...  2023  155270.0
2023-06-01  CAGED12_SALDON12  2023-06-01T00:00:00-03:00  ...  2023  157198.0
2023-07-01  CAGED12_SALDON12  2023-07-01T00:00:00-03:00  ...  2023  142702.0
2023-08-01  CAGED12_SALDON12  2023-08-01T00:00:00-03:00  ...  2023  220844.0
2023-09-01  CAGED12_SALDON12  2023-09-01T00:00:00-03:00  ...  2023  211764.0
2023-10-01  CAGED12_SALDON12  2023-10-01T00:00:00-03:00  ...  2023  190366.0
2023-11-01  CAGED12_SALDON12  2023-11-01T00:00:00-03:00  ...  2023  130097.0
2023-12-01  CAGED12_SALDON12  2023-12-01T00:00:00-03:00  ...  2023 -430159.0
2024-01-01  CAGED12_SALDON12  2024-01-01T00:00:00-03:00  ...  2024  180395.0
2024-02-01  CAGED12_SALDON12  2024-02-01T00:00:00-03:00  ...  2024  306111.0
2024-03-01  CAGED12_SALDON12  2024-03-01T00:00:00-03:00  ...  2024  244315.0

[51 rows x 6 columns]
tabela_idp = (
  dados_sgs
  .reset_index()
  .rename(columns = {"Date": "data"})
  .set_index("data")
)
tabela_idp
                idp
data               
2019-11-01  74643.3
2019-12-01  69174.4
2020-01-01  69231.7
2020-02-01  65058.0
2020-03-01  69231.5
2020-04-01  65494.1
2020-05-01  59698.7
2020-06-01  63600.5
2020-07-01  62835.5
2020-08-01  53665.2
2020-09-01  50999.2
2020-10-01  46394.8
2020-11-01  39992.6
2020-12-01  38270.1
2021-01-01  38830.1
2021-02-01  45111.8
2021-03-01  46666.4
2021-04-01  50173.3
2021-05-01  48949.6
2021-06-01  43533.0
2021-07-01  46549.1
2021-08-01  50691.8
2021-09-01  50263.7
2021-10-01  50022.6
2021-11-01  52720.8
2021-12-01  46440.5
2022-01-01  47080.9
2022-02-01  47777.3
2022-03-01  45332.3
2022-04-01  51768.7
2022-05-01  53901.4
2022-06-01  57702.0
2022-07-01  57034.4
2022-08-01  60576.8
2022-09-01  67313.8
2022-10-01  68393.6
2022-11-01  69906.9
2022-12-01  74606.4
2023-01-01  77099.4
2023-02-01  74833.6
2023-03-01  75258.3
2023-04-01  67399.0
2023-05-01  68317.9
2023-06-01  66992.4
2023-07-01  66916.6
2023-08-01  64444.0
2023-09-01  60436.5
2023-10-01  60360.3
2023-11-01  62197.4
2023-12-01  64229.7
2024-01-01  66439.5
2024-02-01  64283.4
2024-03-01  66529.8

Para fazer o cruzamento usamos a função join() do pandas, usando a sintaxe tabela_x.join(other = tabela_y, how = "inner"). Por padrão o index das tabelas será usado como chave.

# Inner Join: cruzar tabelas mantendo todas as linhas "em comum" de x e y
tabela_caged.join(other = tabela_idp, how = "inner")
                        CODE                   RAW DATE  ...     caged      idp
data                                                     ...                   
2020-01-01  CAGED12_SALDON12  2020-01-01T00:00:00-03:00  ...   92678.0  69231.7
2020-02-01  CAGED12_SALDON12  2020-02-01T00:00:00-03:00  ...  198368.0  65058.0
2020-03-01  CAGED12_SALDON12  2020-03-01T00:00:00-03:00  ... -232316.0  69231.5
2020-04-01  CAGED12_SALDON12  2020-04-01T00:00:00-03:00  ... -902317.0  65494.1
2020-05-01  CAGED12_SALDON12  2020-05-01T00:00:00-03:00  ... -352790.0  59698.7
2020-06-01  CAGED12_SALDON12  2020-06-01T00:00:00-03:00  ...  -23111.0  63600.5
2020-07-01  CAGED12_SALDON12  2020-07-01T00:00:00-03:00  ...  123297.0  62835.5
2020-08-01  CAGED12_SALDON12  2020-08-01T00:00:00-03:00  ...  238125.0  53665.2
2020-09-01  CAGED12_SALDON12  2020-09-01T00:00:00-03:00  ...  313564.0  50999.2
2020-10-01  CAGED12_SALDON12  2020-10-01T00:00:00-03:00  ...  388938.0  46394.8
2020-11-01  CAGED12_SALDON12  2020-11-01T00:00:00-03:00  ...  408948.0  39992.6
2020-12-01  CAGED12_SALDON12  2020-12-01T00:00:00-03:00  ...  -82118.0  38270.1
2021-01-01  CAGED12_SALDON12  2021-01-01T00:00:00-03:00  ...  257059.0  38830.1
2021-02-01  CAGED12_SALDON12  2021-02-01T00:00:00-03:00  ...  397915.0  45111.8
2021-03-01  CAGED12_SALDON12  2021-03-01T00:00:00-03:00  ...  176330.0  46666.4
2021-04-01  CAGED12_SALDON12  2021-04-01T00:00:00-03:00  ...  113922.0  50173.3
2021-05-01  CAGED12_SALDON12  2021-05-01T00:00:00-03:00  ...  278705.0  48949.6
2021-06-01  CAGED12_SALDON12  2021-06-01T00:00:00-03:00  ...  310036.0  43533.0
2021-07-01  CAGED12_SALDON12  2021-07-01T00:00:00-03:00  ...  316725.0  46549.1
2021-08-01  CAGED12_SALDON12  2021-08-01T00:00:00-03:00  ...  373344.0  50691.8
2021-09-01  CAGED12_SALDON12  2021-09-01T00:00:00-03:00  ...  316993.0  50263.7
2021-10-01  CAGED12_SALDON12  2021-10-01T00:00:00-03:00  ...  253083.0  50022.6
2021-11-01  CAGED12_SALDON12  2021-11-01T00:00:00-03:00  ...  324112.0  52720.8
2021-12-01  CAGED12_SALDON12  2021-12-01T00:00:00-03:00  ... -265811.0  46440.5
2022-01-01  CAGED12_SALDON12  2022-01-01T00:00:00-03:00  ...  155178.0  47080.9
2022-02-01  CAGED12_SALDON12  2022-02-01T00:00:00-03:00  ...  328507.0  47777.3
2022-03-01  CAGED12_SALDON12  2022-03-01T00:00:00-03:00  ...  136189.0  45332.3
2022-04-01  CAGED12_SALDON12  2022-04-01T00:00:00-03:00  ...  196966.0  51768.7
2022-05-01  CAGED12_SALDON12  2022-05-01T00:00:00-03:00  ...  277018.0  53901.4
2022-06-01  CAGED12_SALDON12  2022-06-01T00:00:00-03:00  ...  277944.0  57702.0
2022-07-01  CAGED12_SALDON12  2022-07-01T00:00:00-03:00  ...  218902.0  57034.4
2022-08-01  CAGED12_SALDON12  2022-08-01T00:00:00-03:00  ...  278639.0  60576.8
2022-09-01  CAGED12_SALDON12  2022-09-01T00:00:00-03:00  ...  278085.0  67313.8
2022-10-01  CAGED12_SALDON12  2022-10-01T00:00:00-03:00  ...  159454.0  68393.6
2022-11-01  CAGED12_SALDON12  2022-11-01T00:00:00-03:00  ...  135495.0  69906.9
2022-12-01  CAGED12_SALDON12  2022-12-01T00:00:00-03:00  ... -431011.0  74606.4
2023-01-01  CAGED12_SALDON12  2023-01-01T00:00:00-03:00  ...   83297.0  77099.4
2023-02-01  CAGED12_SALDON12  2023-02-01T00:00:00-03:00  ...  241785.0  74833.6
2023-03-01  CAGED12_SALDON12  2023-03-01T00:00:00-03:00  ...  195171.0  75258.3
2023-04-01  CAGED12_SALDON12  2023-04-01T00:00:00-03:00  ...  180005.0  67399.0
2023-05-01  CAGED12_SALDON12  2023-05-01T00:00:00-03:00  ...  155270.0  68317.9
2023-06-01  CAGED12_SALDON12  2023-06-01T00:00:00-03:00  ...  157198.0  66992.4
2023-07-01  CAGED12_SALDON12  2023-07-01T00:00:00-03:00  ...  142702.0  66916.6
2023-08-01  CAGED12_SALDON12  2023-08-01T00:00:00-03:00  ...  220844.0  64444.0
2023-09-01  CAGED12_SALDON12  2023-09-01T00:00:00-03:00  ...  211764.0  60436.5
2023-10-01  CAGED12_SALDON12  2023-10-01T00:00:00-03:00  ...  190366.0  60360.3
2023-11-01  CAGED12_SALDON12  2023-11-01T00:00:00-03:00  ...  130097.0  62197.4
2023-12-01  CAGED12_SALDON12  2023-12-01T00:00:00-03:00  ... -430159.0  64229.7
2024-01-01  CAGED12_SALDON12  2024-01-01T00:00:00-03:00  ...  180395.0  66439.5
2024-02-01  CAGED12_SALDON12  2024-02-01T00:00:00-03:00  ...  306111.0  64283.4
2024-03-01  CAGED12_SALDON12  2024-03-01T00:00:00-03:00  ...  244315.0  66529.8

[51 rows x 7 columns]

No cruzamento do tipo inner join é bastante fácil perder observações que poderiam ser importantes para sua análise, pois as linhas sem correspondência não são incluídas no resultado final.

Cruzamentos outer join

O mais utilizado é o left join.

# Left Join: cruzar tabelas mantendo todas as linhas de x
tabela_caged.join(other = tabela_idp, how = "left")
                        CODE                   RAW DATE  ...     caged      idp
data                                                     ...                   
2020-01-01  CAGED12_SALDON12  2020-01-01T00:00:00-03:00  ...   92678.0  69231.7
2020-02-01  CAGED12_SALDON12  2020-02-01T00:00:00-03:00  ...  198368.0  65058.0
2020-03-01  CAGED12_SALDON12  2020-03-01T00:00:00-03:00  ... -232316.0  69231.5
2020-04-01  CAGED12_SALDON12  2020-04-01T00:00:00-03:00  ... -902317.0  65494.1
2020-05-01  CAGED12_SALDON12  2020-05-01T00:00:00-03:00  ... -352790.0  59698.7
2020-06-01  CAGED12_SALDON12  2020-06-01T00:00:00-03:00  ...  -23111.0  63600.5
2020-07-01  CAGED12_SALDON12  2020-07-01T00:00:00-03:00  ...  123297.0  62835.5
2020-08-01  CAGED12_SALDON12  2020-08-01T00:00:00-03:00  ...  238125.0  53665.2
2020-09-01  CAGED12_SALDON12  2020-09-01T00:00:00-03:00  ...  313564.0  50999.2
2020-10-01  CAGED12_SALDON12  2020-10-01T00:00:00-03:00  ...  388938.0  46394.8
2020-11-01  CAGED12_SALDON12  2020-11-01T00:00:00-03:00  ...  408948.0  39992.6
2020-12-01  CAGED12_SALDON12  2020-12-01T00:00:00-03:00  ...  -82118.0  38270.1
2021-01-01  CAGED12_SALDON12  2021-01-01T00:00:00-03:00  ...  257059.0  38830.1
2021-02-01  CAGED12_SALDON12  2021-02-01T00:00:00-03:00  ...  397915.0  45111.8
2021-03-01  CAGED12_SALDON12  2021-03-01T00:00:00-03:00  ...  176330.0  46666.4
2021-04-01  CAGED12_SALDON12  2021-04-01T00:00:00-03:00  ...  113922.0  50173.3
2021-05-01  CAGED12_SALDON12  2021-05-01T00:00:00-03:00  ...  278705.0  48949.6
2021-06-01  CAGED12_SALDON12  2021-06-01T00:00:00-03:00  ...  310036.0  43533.0
2021-07-01  CAGED12_SALDON12  2021-07-01T00:00:00-03:00  ...  316725.0  46549.1
2021-08-01  CAGED12_SALDON12  2021-08-01T00:00:00-03:00  ...  373344.0  50691.8
2021-09-01  CAGED12_SALDON12  2021-09-01T00:00:00-03:00  ...  316993.0  50263.7
2021-10-01  CAGED12_SALDON12  2021-10-01T00:00:00-03:00  ...  253083.0  50022.6
2021-11-01  CAGED12_SALDON12  2021-11-01T00:00:00-03:00  ...  324112.0  52720.8
2021-12-01  CAGED12_SALDON12  2021-12-01T00:00:00-03:00  ... -265811.0  46440.5
2022-01-01  CAGED12_SALDON12  2022-01-01T00:00:00-03:00  ...  155178.0  47080.9
2022-02-01  CAGED12_SALDON12  2022-02-01T00:00:00-03:00  ...  328507.0  47777.3
2022-03-01  CAGED12_SALDON12  2022-03-01T00:00:00-03:00  ...  136189.0  45332.3
2022-04-01  CAGED12_SALDON12  2022-04-01T00:00:00-03:00  ...  196966.0  51768.7
2022-05-01  CAGED12_SALDON12  2022-05-01T00:00:00-03:00  ...  277018.0  53901.4
2022-06-01  CAGED12_SALDON12  2022-06-01T00:00:00-03:00  ...  277944.0  57702.0
2022-07-01  CAGED12_SALDON12  2022-07-01T00:00:00-03:00  ...  218902.0  57034.4
2022-08-01  CAGED12_SALDON12  2022-08-01T00:00:00-03:00  ...  278639.0  60576.8
2022-09-01  CAGED12_SALDON12  2022-09-01T00:00:00-03:00  ...  278085.0  67313.8
2022-10-01  CAGED12_SALDON12  2022-10-01T00:00:00-03:00  ...  159454.0  68393.6
2022-11-01  CAGED12_SALDON12  2022-11-01T00:00:00-03:00  ...  135495.0  69906.9
2022-12-01  CAGED12_SALDON12  2022-12-01T00:00:00-03:00  ... -431011.0  74606.4
2023-01-01  CAGED12_SALDON12  2023-01-01T00:00:00-03:00  ...   83297.0  77099.4
2023-02-01  CAGED12_SALDON12  2023-02-01T00:00:00-03:00  ...  241785.0  74833.6
2023-03-01  CAGED12_SALDON12  2023-03-01T00:00:00-03:00  ...  195171.0  75258.3
2023-04-01  CAGED12_SALDON12  2023-04-01T00:00:00-03:00  ...  180005.0  67399.0
2023-05-01  CAGED12_SALDON12  2023-05-01T00:00:00-03:00  ...  155270.0  68317.9
2023-06-01  CAGED12_SALDON12  2023-06-01T00:00:00-03:00  ...  157198.0  66992.4
2023-07-01  CAGED12_SALDON12  2023-07-01T00:00:00-03:00  ...  142702.0  66916.6
2023-08-01  CAGED12_SALDON12  2023-08-01T00:00:00-03:00  ...  220844.0  64444.0
2023-09-01  CAGED12_SALDON12  2023-09-01T00:00:00-03:00  ...  211764.0  60436.5
2023-10-01  CAGED12_SALDON12  2023-10-01T00:00:00-03:00  ...  190366.0  60360.3
2023-11-01  CAGED12_SALDON12  2023-11-01T00:00:00-03:00  ...  130097.0  62197.4
2023-12-01  CAGED12_SALDON12  2023-12-01T00:00:00-03:00  ... -430159.0  64229.7
2024-01-01  CAGED12_SALDON12  2024-01-01T00:00:00-03:00  ...  180395.0  66439.5
2024-02-01  CAGED12_SALDON12  2024-02-01T00:00:00-03:00  ...  306111.0  64283.4
2024-03-01  CAGED12_SALDON12  2024-03-01T00:00:00-03:00  ...  244315.0  66529.8

[51 rows x 7 columns]

Por sua vez, o right join:

# Right Join: cruzar tabelas mantendo todas as linhas de y
tabela_caged.join(other = tabela_idp, how = "right")
                        CODE                   RAW DATE  ...     caged      idp
data                                                     ...                   
2019-11-01               NaN                        NaN  ...       NaN  74643.3
2019-12-01               NaN                        NaN  ...       NaN  69174.4
2020-01-01  CAGED12_SALDON12  2020-01-01T00:00:00-03:00  ...   92678.0  69231.7
2020-02-01  CAGED12_SALDON12  2020-02-01T00:00:00-03:00  ...  198368.0  65058.0
2020-03-01  CAGED12_SALDON12  2020-03-01T00:00:00-03:00  ... -232316.0  69231.5
2020-04-01  CAGED12_SALDON12  2020-04-01T00:00:00-03:00  ... -902317.0  65494.1
2020-05-01  CAGED12_SALDON12  2020-05-01T00:00:00-03:00  ... -352790.0  59698.7
2020-06-01  CAGED12_SALDON12  2020-06-01T00:00:00-03:00  ...  -23111.0  63600.5
2020-07-01  CAGED12_SALDON12  2020-07-01T00:00:00-03:00  ...  123297.0  62835.5
2020-08-01  CAGED12_SALDON12  2020-08-01T00:00:00-03:00  ...  238125.0  53665.2
2020-09-01  CAGED12_SALDON12  2020-09-01T00:00:00-03:00  ...  313564.0  50999.2
2020-10-01  CAGED12_SALDON12  2020-10-01T00:00:00-03:00  ...  388938.0  46394.8
2020-11-01  CAGED12_SALDON12  2020-11-01T00:00:00-03:00  ...  408948.0  39992.6
2020-12-01  CAGED12_SALDON12  2020-12-01T00:00:00-03:00  ...  -82118.0  38270.1
2021-01-01  CAGED12_SALDON12  2021-01-01T00:00:00-03:00  ...  257059.0  38830.1
2021-02-01  CAGED12_SALDON12  2021-02-01T00:00:00-03:00  ...  397915.0  45111.8
2021-03-01  CAGED12_SALDON12  2021-03-01T00:00:00-03:00  ...  176330.0  46666.4
2021-04-01  CAGED12_SALDON12  2021-04-01T00:00:00-03:00  ...  113922.0  50173.3
2021-05-01  CAGED12_SALDON12  2021-05-01T00:00:00-03:00  ...  278705.0  48949.6
2021-06-01  CAGED12_SALDON12  2021-06-01T00:00:00-03:00  ...  310036.0  43533.0
2021-07-01  CAGED12_SALDON12  2021-07-01T00:00:00-03:00  ...  316725.0  46549.1
2021-08-01  CAGED12_SALDON12  2021-08-01T00:00:00-03:00  ...  373344.0  50691.8
2021-09-01  CAGED12_SALDON12  2021-09-01T00:00:00-03:00  ...  316993.0  50263.7
2021-10-01  CAGED12_SALDON12  2021-10-01T00:00:00-03:00  ...  253083.0  50022.6
2021-11-01  CAGED12_SALDON12  2021-11-01T00:00:00-03:00  ...  324112.0  52720.8
2021-12-01  CAGED12_SALDON12  2021-12-01T00:00:00-03:00  ... -265811.0  46440.5
2022-01-01  CAGED12_SALDON12  2022-01-01T00:00:00-03:00  ...  155178.0  47080.9
2022-02-01  CAGED12_SALDON12  2022-02-01T00:00:00-03:00  ...  328507.0  47777.3
2022-03-01  CAGED12_SALDON12  2022-03-01T00:00:00-03:00  ...  136189.0  45332.3
2022-04-01  CAGED12_SALDON12  2022-04-01T00:00:00-03:00  ...  196966.0  51768.7
2022-05-01  CAGED12_SALDON12  2022-05-01T00:00:00-03:00  ...  277018.0  53901.4
2022-06-01  CAGED12_SALDON12  2022-06-01T00:00:00-03:00  ...  277944.0  57702.0
2022-07-01  CAGED12_SALDON12  2022-07-01T00:00:00-03:00  ...  218902.0  57034.4
2022-08-01  CAGED12_SALDON12  2022-08-01T00:00:00-03:00  ...  278639.0  60576.8
2022-09-01  CAGED12_SALDON12  2022-09-01T00:00:00-03:00  ...  278085.0  67313.8
2022-10-01  CAGED12_SALDON12  2022-10-01T00:00:00-03:00  ...  159454.0  68393.6
2022-11-01  CAGED12_SALDON12  2022-11-01T00:00:00-03:00  ...  135495.0  69906.9
2022-12-01  CAGED12_SALDON12  2022-12-01T00:00:00-03:00  ... -431011.0  74606.4
2023-01-01  CAGED12_SALDON12  2023-01-01T00:00:00-03:00  ...   83297.0  77099.4
2023-02-01  CAGED12_SALDON12  2023-02-01T00:00:00-03:00  ...  241785.0  74833.6
2023-03-01  CAGED12_SALDON12  2023-03-01T00:00:00-03:00  ...  195171.0  75258.3
2023-04-01  CAGED12_SALDON12  2023-04-01T00:00:00-03:00  ...  180005.0  67399.0
2023-05-01  CAGED12_SALDON12  2023-05-01T00:00:00-03:00  ...  155270.0  68317.9
2023-06-01  CAGED12_SALDON12  2023-06-01T00:00:00-03:00  ...  157198.0  66992.4
2023-07-01  CAGED12_SALDON12  2023-07-01T00:00:00-03:00  ...  142702.0  66916.6
2023-08-01  CAGED12_SALDON12  2023-08-01T00:00:00-03:00  ...  220844.0  64444.0
2023-09-01  CAGED12_SALDON12  2023-09-01T00:00:00-03:00  ...  211764.0  60436.5
2023-10-01  CAGED12_SALDON12  2023-10-01T00:00:00-03:00  ...  190366.0  60360.3
2023-11-01  CAGED12_SALDON12  2023-11-01T00:00:00-03:00  ...  130097.0  62197.4
2023-12-01  CAGED12_SALDON12  2023-12-01T00:00:00-03:00  ... -430159.0  64229.7
2024-01-01  CAGED12_SALDON12  2024-01-01T00:00:00-03:00  ...  180395.0  66439.5
2024-02-01  CAGED12_SALDON12  2024-02-01T00:00:00-03:00  ...  306111.0  64283.4
2024-03-01  CAGED12_SALDON12  2024-03-01T00:00:00-03:00  ...  244315.0  66529.8

[53 rows x 7 columns]

E por último, o full join:

# Full Join: cruzar tabelas mantendo todas as linhas de x ou y
tabela_caged.join(other = tabela_idp, how = "outer")
                        CODE                   RAW DATE  ...     caged      idp
data                                                     ...                   
2019-11-01               NaN                        NaN  ...       NaN  74643.3
2019-12-01               NaN                        NaN  ...       NaN  69174.4
2020-01-01  CAGED12_SALDON12  2020-01-01T00:00:00-03:00  ...   92678.0  69231.7
2020-02-01  CAGED12_SALDON12  2020-02-01T00:00:00-03:00  ...  198368.0  65058.0
2020-03-01  CAGED12_SALDON12  2020-03-01T00:00:00-03:00  ... -232316.0  69231.5
2020-04-01  CAGED12_SALDON12  2020-04-01T00:00:00-03:00  ... -902317.0  65494.1
2020-05-01  CAGED12_SALDON12  2020-05-01T00:00:00-03:00  ... -352790.0  59698.7
2020-06-01  CAGED12_SALDON12  2020-06-01T00:00:00-03:00  ...  -23111.0  63600.5
2020-07-01  CAGED12_SALDON12  2020-07-01T00:00:00-03:00  ...  123297.0  62835.5
2020-08-01  CAGED12_SALDON12  2020-08-01T00:00:00-03:00  ...  238125.0  53665.2
2020-09-01  CAGED12_SALDON12  2020-09-01T00:00:00-03:00  ...  313564.0  50999.2
2020-10-01  CAGED12_SALDON12  2020-10-01T00:00:00-03:00  ...  388938.0  46394.8
2020-11-01  CAGED12_SALDON12  2020-11-01T00:00:00-03:00  ...  408948.0  39992.6
2020-12-01  CAGED12_SALDON12  2020-12-01T00:00:00-03:00  ...  -82118.0  38270.1
2021-01-01  CAGED12_SALDON12  2021-01-01T00:00:00-03:00  ...  257059.0  38830.1
2021-02-01  CAGED12_SALDON12  2021-02-01T00:00:00-03:00  ...  397915.0  45111.8
2021-03-01  CAGED12_SALDON12  2021-03-01T00:00:00-03:00  ...  176330.0  46666.4
2021-04-01  CAGED12_SALDON12  2021-04-01T00:00:00-03:00  ...  113922.0  50173.3
2021-05-01  CAGED12_SALDON12  2021-05-01T00:00:00-03:00  ...  278705.0  48949.6
2021-06-01  CAGED12_SALDON12  2021-06-01T00:00:00-03:00  ...  310036.0  43533.0
2021-07-01  CAGED12_SALDON12  2021-07-01T00:00:00-03:00  ...  316725.0  46549.1
2021-08-01  CAGED12_SALDON12  2021-08-01T00:00:00-03:00  ...  373344.0  50691.8
2021-09-01  CAGED12_SALDON12  2021-09-01T00:00:00-03:00  ...  316993.0  50263.7
2021-10-01  CAGED12_SALDON12  2021-10-01T00:00:00-03:00  ...  253083.0  50022.6
2021-11-01  CAGED12_SALDON12  2021-11-01T00:00:00-03:00  ...  324112.0  52720.8
2021-12-01  CAGED12_SALDON12  2021-12-01T00:00:00-03:00  ... -265811.0  46440.5
2022-01-01  CAGED12_SALDON12  2022-01-01T00:00:00-03:00  ...  155178.0  47080.9
2022-02-01  CAGED12_SALDON12  2022-02-01T00:00:00-03:00  ...  328507.0  47777.3
2022-03-01  CAGED12_SALDON12  2022-03-01T00:00:00-03:00  ...  136189.0  45332.3
2022-04-01  CAGED12_SALDON12  2022-04-01T00:00:00-03:00  ...  196966.0  51768.7
2022-05-01  CAGED12_SALDON12  2022-05-01T00:00:00-03:00  ...  277018.0  53901.4
2022-06-01  CAGED12_SALDON12  2022-06-01T00:00:00-03:00  ...  277944.0  57702.0
2022-07-01  CAGED12_SALDON12  2022-07-01T00:00:00-03:00  ...  218902.0  57034.4
2022-08-01  CAGED12_SALDON12  2022-08-01T00:00:00-03:00  ...  278639.0  60576.8
2022-09-01  CAGED12_SALDON12  2022-09-01T00:00:00-03:00  ...  278085.0  67313.8
2022-10-01  CAGED12_SALDON12  2022-10-01T00:00:00-03:00  ...  159454.0  68393.6
2022-11-01  CAGED12_SALDON12  2022-11-01T00:00:00-03:00  ...  135495.0  69906.9
2022-12-01  CAGED12_SALDON12  2022-12-01T00:00:00-03:00  ... -431011.0  74606.4
2023-01-01  CAGED12_SALDON12  2023-01-01T00:00:00-03:00  ...   83297.0  77099.4
2023-02-01  CAGED12_SALDON12  2023-02-01T00:00:00-03:00  ...  241785.0  74833.6
2023-03-01  CAGED12_SALDON12  2023-03-01T00:00:00-03:00  ...  195171.0  75258.3
2023-04-01  CAGED12_SALDON12  2023-04-01T00:00:00-03:00  ...  180005.0  67399.0
2023-05-01  CAGED12_SALDON12  2023-05-01T00:00:00-03:00  ...  155270.0  68317.9
2023-06-01  CAGED12_SALDON12  2023-06-01T00:00:00-03:00  ...  157198.0  66992.4
2023-07-01  CAGED12_SALDON12  2023-07-01T00:00:00-03:00  ...  142702.0  66916.6
2023-08-01  CAGED12_SALDON12  2023-08-01T00:00:00-03:00  ...  220844.0  64444.0
2023-09-01  CAGED12_SALDON12  2023-09-01T00:00:00-03:00  ...  211764.0  60436.5
2023-10-01  CAGED12_SALDON12  2023-10-01T00:00:00-03:00  ...  190366.0  60360.3
2023-11-01  CAGED12_SALDON12  2023-11-01T00:00:00-03:00  ...  130097.0  62197.4
2023-12-01  CAGED12_SALDON12  2023-12-01T00:00:00-03:00  ... -430159.0  64229.7
2024-01-01  CAGED12_SALDON12  2024-01-01T00:00:00-03:00  ...  180395.0  66439.5
2024-02-01  CAGED12_SALDON12  2024-02-01T00:00:00-03:00  ...  306111.0  64283.4
2024-03-01  CAGED12_SALDON12  2024-03-01T00:00:00-03:00  ...  244315.0  66529.8

[53 rows x 7 columns]

Trabalhando com Datas no Python

No Python, pode-se definir os tipos de dados para datas para os seguintes tipos:

  • data: armazena uma data de calendário
  • time: armazena a hora do dia na forma de horas, minutos, segundos e microssegundos
  • datetime: armazena data e hora
  • timedelta: diferença entre dois valores de datas
  • tzinfo: fuso horário (time zone)

Para especificação de formatos de datas, é comum usar o padrão %Y para ano com quatro dígitos, %m para mês com dois dígitos e %d para dia com dois dígitos, formando o padrão %Y-%m-%d, comum para a importação de diversos dados em séries temporais ao redor do mundo. Ainda é possível que seja adicionado %H, que representa as horas em formato de 24h.

Existem diversos outros formatos para anos, meses, dias e horas, seguindo a lista abaixo:

ano

  • %y: ano de dois dígitos (22)
  • %Y: ano de quatro dígitos (2022)

mês

  • %b: Mês abreviado (Mar)
  • %B: Nomes completos de meses (March)
  • %m: Mês como número (03 ou 3)

dia

  • %d: Dia do mês (30)

dia da semana

  • %a: Nome abreviado (Wed)
  • %A: Nome completo
  • %w: número (3 - Sunday sempre como 0 até Saturday como 6)

horas

  • %H: 24 horas (00 até 23)
  • %I: 12 horas (01 até 12)
  • %M: minutos (01 até 59)

segundos

  • %S: segundos de 00 até 59

AM/PM

  • %p: utilizado junto com horas em formatos de 12 horas

Existem diversas formas de representar datas no Python, a mais comum é o formato internacional “YYYY-MM-DD”, padrão na biblioteca pandas.

# Importar bibliotecas
import pandas as pd
import datetime
import numpy as np

hoje = pd.to_datetime("2024-05-27")
hoje
Timestamp('2024-05-27 00:00:00')

O tipo de um objeto escalar neste formato é Timestamp e em uma pandas Series é datetime64[ns]:

type(hoje)
<class 'pandas._libs.tslibs.timestamps.Timestamp'>
pd.Series(hoje).info()
<class 'pandas.core.series.Series'>
RangeIndex: 1 entries, 0 to 0
Series name: None
Non-Null Count  Dtype         
--------------  -----         
1 non-null      datetime64[ns]
dtypes: datetime64[ns](1)
memory usage: 140.0 bytes

Criando datas com diferentes formatos

A função to_datetime() possibilita criar datas e interpreta formatos variados e não padronizados. Por exemplo:

# Cria um objeto datetime do pandas a partir de datas de variados formatos
pd.to_datetime(["2024-05-27", "2024/05/28", "20240529", np.datetime64("2024-05-26"), datetime.datetime(2024, 5, 27)], format='mixed')
DatetimeIndex(['2024-05-27', '2024-05-28', '2024-05-29', '2024-05-26',
               '2024-05-27'],
              dtype='datetime64[ns]', freq=None)

Se precisar, e em caso de erros, é importante especificar o formato atual da data (link) para que a função consiga convertê-la para o padrão internacional:

pd.to_datetime("28/02/2023", format = "%d/%m/%Y")
Timestamp('2023-02-28 00:00:00')

Alterando a representação da data

Podemos mudar a representação da data para um formato diferente, conforme a necessidade, usando to_period() e to_timestamp(). Por exemplo:

# Converte data para outras frequências
hoje.to_period(freq = "M") # mensal
Period('2024-05', 'M')
hoje.to_period(freq = "Q") # trimestral
Period('2024Q2', 'Q-DEC')
hoje.to_period(freq = "Y") # anual
Period('2024', 'Y-DEC')
# Retorna data para formato internacional
hoje.to_period(freq = "M").to_timestamp(freq = "D") # converte para YYYY-MM-DD 
Timestamp('2024-05-01 00:00:00')
hoje.to_period(freq = "Q").to_timestamp(freq = "D", how = "End") # converte para YYYY-MM-DD com dia no final do período
Timestamp('2024-06-30 23:59:59.999999999')

Criando sequências de datas

Com a função date_range() é possível criar sequências de datas com frequência fixa (neste link a lista de códigos de frequências úteis). Por exemplo:

# Sequência de datas de frequência mensal de início a fim
pd.date_range(start = "2023-01-01", end = "2023-12-01", freq = "MS")
DatetimeIndex(['2023-01-01', '2023-02-01', '2023-03-01', '2023-04-01',
               '2023-05-01', '2023-06-01', '2023-07-01', '2023-08-01',
               '2023-09-01', '2023-10-01', '2023-11-01', '2023-12-01'],
              dtype='datetime64[ns]', freq='MS')
# Sequência de datas de frequência mensal de início + nº de períodos
pd.date_range(start = "2023-01-01", periods = 12, freq = "MS")
DatetimeIndex(['2023-01-01', '2023-02-01', '2023-03-01', '2023-04-01',
               '2023-05-01', '2023-06-01', '2023-07-01', '2023-08-01',
               '2023-09-01', '2023-10-01', '2023-11-01', '2023-12-01'],
              dtype='datetime64[ns]', freq='MS')

Componentes de datas

Existem diversos componentes úteis que podem ser acessados de um Timestamp ou uma coleção de Timestamp, como um DatetimeIndex:

# Cria sequência de datas
datas = pd.date_range(start = "2023-01-01", periods = 2, freq = "MS")

Extrai componentes das datas:

datas.year   # ano
Index([2023, 2023], dtype='int32')
datas.month  # mês
Index([1, 2], dtype='int32')
datas.day    # dia
Index([1, 1], dtype='int32')

Em caso de ter uma pandas Series com datas, use o acessor .dt para acessar estes componentes:

pd.Series(datas).dt.month
0    1
1    2
dtype: int32

Identificando e Tratando Dados Faltantes

# Importar bibliotecas
import pandas as pd
import numpy as np
import ipeadatapy as ipea
from bcb import sgs

# Dados do saldo do CAGED (Ipeadata)
dados_ipeadata = (
  ipea.timeseries("CAGED12_SALDON12")
  .rename_axis("data", axis = "index")
  .rename(columns = {"VALUE (Pessoa)": "caged"})
  .filter(items = ["caged"], axis = "columns")
  .query("data <= '2020-06-01'")
)

# Dados do IDP/BP - acum. 12m - US$ (milhões) (SGS/BCB)
dados_sgs = (
  sgs.get(
    codes = {"idp": 24422}, 
    start = "2019-11-01", 
    end = "2020-03-01")
  .rename_axis("data", axis = "index")
)

# Cruzamento de dados
tabela = dados_ipeadata.join(other = dados_sgs, how = "outer")
tabela
               caged      idp
data                         
2019-11-01       NaN  74643.3
2019-12-01       NaN  69174.4
2020-01-01   92678.0  69231.7
2020-02-01  198368.0  65058.0
2020-03-01 -232316.0  69231.5
2020-04-01 -902317.0      NaN
2020-05-01 -352790.0      NaN
2020-06-01  -23111.0      NaN

Como identificar se há NaN em uma tabela? Por código, basta usar isna() sobre uma coluna ou DataFrame, o retorno é True ou False, ou fazer um filtro para trazer somentes os casos verdadeiros:

# Testa quais observações são NaN
tabela.isna()
            caged    idp
data                    
2019-11-01   True  False
2019-12-01   True  False
2020-01-01  False  False
2020-02-01  False  False
2020-03-01  False  False
2020-04-01  False   True
2020-05-01  False   True
2020-06-01  False   True
# Filtrando linhas com NaN (uma única coluna)
tabela.query("caged.isna()")
            caged      idp
data                      
2019-11-01    NaN  74643.3
2019-12-01    NaN  69174.4

A mesma lógica pode ser seguida para mais de uma coluna. Neste caso, o operador or pode ser útil no filtro, pois retorna valores lógicos True para quaisquer das linhas das colunas especificadas quem possuam NaN:

# Filtrando linhas com NaN (+ de 1 coluna)
tabela.query("caged.isna() or idp.isna()")
               caged      idp
data                         
2019-11-01       NaN  74643.3
2019-12-01       NaN  69174.4
2020-04-01 -902317.0      NaN
2020-05-01 -352790.0      NaN
2020-06-01  -23111.0      NaN

Caso não senqueira apenas identificar quais linhas de quais colunas possuem NaN, mas sim contabilizar os NaN por cada variável, deve-se usar um for-loop: a ideia é somar o número de NaN por coluna, a função lambda np.sum(x.isna()) faz isso. Optou-se por usar a função apply() para o loop, mas é possível usar abordagens diferentes e obter os mesmos resultados:

# Quantos NaN existem por coluna? Use um for-loop:
tabela.apply(lambda x: np.sum(x.isna()))
caged    2
idp      3
dtype: int64

O mesmo resultado poderia ter sido obtido usando outras ferramentas do pandas:

tabela.agg(func = lambda x: np.sum(x.isna()), axis = "index")
caged    2
idp      3
dtype: int64

Tratando NaN: remoção

Se foram identificada as linhas com NaN em uma coluna e o objetivo é removê-las, deve-se usar a função dropna():

# Removendo linhas com NaN (de uma única coluna)
tabela.dropna(subset = ["caged"])
               caged      idp
data                         
2020-01-01   92678.0  69231.7
2020-02-01  198368.0  65058.0
2020-03-01 -232316.0  69231.5
2020-04-01 -902317.0      NaN
2020-05-01 -352790.0      NaN
2020-06-01  -23111.0      NaN
# Removendo linhas com NaN (de todas as colunas)
tabela.dropna()
               caged      idp
data                         
2020-01-01   92678.0  69231.7
2020-02-01  198368.0  65058.0
2020-03-01 -232316.0  69231.5

Tratando NaN: substituição

Se remover as linhas com NaN não for viável, deve-se substituir as obsevações ausentes por outros valores. Os valores que entrarão no lugar dos NaN são definidos por critério do usuário, mas é comum usar a média da variável ou o valor da observação anterior (para séries temporais). Isso pode ser feito com a função fillna() do pandas:

( # Substituindo valores NaN
  tabela
  # substituir NaN pela média histórica
  .fillna(value = {"caged": tabela.caged.mean()})
  # substituir NaN pelo último valor observado
  .fillna(method = "ffill")
)
               caged      idp
data                         
2019-11-01 -203248.0  74643.3
2019-12-01 -203248.0  69174.4
2020-01-01   92678.0  69231.7
2020-02-01  198368.0  65058.0
2020-03-01 -232316.0  69231.5
2020-04-01 -902317.0  69231.5
2020-05-01 -352790.0  69231.5
2020-06-01  -23111.0  69231.5

Outra possibilidade é o uso da função combine_first() do pandas que possibilita substituir NaN de uma coluna com base em valores ao lado (de outra coluna):

# Substituindo valores NaN de uma coluna por valores de outra coluna
tabela.assign(
  # substitui NaN de idp por valores em caged
  idp = tabela.idp.combine_first(tabela.caged)
)
               caged       idp
data                          
2019-11-01       NaN   74643.3
2019-12-01       NaN   69174.4
2020-01-01   92678.0   69231.7
2020-02-01  198368.0   65058.0
2020-03-01 -232316.0   69231.5
2020-04-01 -902317.0 -902317.0
2020-05-01 -352790.0 -352790.0
2020-06-01  -23111.0  -23111.0

Note que a substituição acima não faz sentido em uma análise de dados, pois são variáveis distintas, constituindo apenas um exemplo didático.

Como Deflacionar uma série

# Importar bibliotecas
import pandas as pd
import ipeadatapy as ipea

# Dados de valores nominais: salário mínimo vigente (R$, Ipeadata)
dados_nominais = (
  ipea.timeseries("MTE12_SALMIN12")
  .rename_axis("data", axis = "index")
  .rename(columns = {"VALUE (R$)": "nominal"})
  .filter(items = ["nominal"], axis = "columns")
  .query("data >= '2000-01-01'")
)

# Dados de índice de preços: INPC (índice 12/1993 = 100, Sidra/IBGE)
dados_indice = (
  pd.read_json(
    path_or_buf = "https://apisidra.ibge.gov.br/values/t/1736/n1/all/v/2289/p/all/d/v2289%2013?formato=json"
    )
  .rename(columns = {"V": "indice", "D3C": "data"})
  .query("indice != 'Valor'")
  .filter(items = ["data", "indice"])
  .assign(
    data = lambda x: pd.to_datetime(x.data, format = "%Y%m"),
    indice = lambda x: x.indice.astype(float)
    )
  .set_index("data")
)
dados_nominais
            nominal
data               
2000-01-01    136.0
2000-02-01    136.0
2000-03-01    136.0
2000-04-01    151.0
2000-05-01    151.0
...             ...
2024-08-01   1412.0
2024-09-01   1412.0
2024-10-01   1412.0
2024-11-01   1412.0
2024-12-01   1412.0

[300 rows x 1 columns]
dados_indice
                  indice
data                    
1979-03-01  5.576500e-09
1979-04-01  5.768900e-09
1979-05-01  5.870400e-09
1979-06-01  6.046500e-09
1979-07-01  6.370500e-09
...                  ...
2023-12-01  6.954740e+03
2024-01-01  6.994380e+03
2024-02-01  7.051030e+03
2024-03-01  7.064430e+03
2024-04-01  7.090570e+03

[542 rows x 1 columns]

Após ter os dados necessários já tratados, basta cruzar as tabelas e aplicar a fórmula de deflacionamento, que envolve calcular um fator de deflacionamento usando uma data base (neste exemplo é o último mês observado) e multiplicar este fator pela série nominal:

\[Vr_{i:j} = (\frac{I_{j}}{I_{i}}) * V_{i}\] onde:
\(Vr_{i:j}\) é o valor real, ou deflacionado, no período \(i\) na data-base \(j\)
\(I_{j}\) é o índice de preços fixado na data-base \(j\)
\(I_{i}\) é o índice de preços no período \(i\)
\(V_{i}\) é o valor ou preço nominal no período \(i\)

# Cruzar tabelas
tabela = dados_nominais.join(other = dados_indice, how = "inner")

# Deflacionar a série com data base = último mês observado
indice_data_base = tabela.query("data == data.max()").indice.values
tabela.assign(real = lambda x: (indice_data_base / x.indice) * x.nominal).tail()
            nominal   indice         real
data                                     
2023-12-01   1320.0  6954.74  1345.780345
2024-01-01   1412.0  6994.38  1431.418487
2024-02-01   1412.0  7051.03  1419.918060
2024-03-01   1412.0  7064.43  1417.224722
2024-04-01   1412.0  7090.57  1412.000000

Como fazer um ajuste sazonal?

# Importar bibliotecas
#pip install python-dateutil
from statsmodels.tsa.seasonal import seasonal_decompose
from dateutil.parser import parse
import pandas as pd

dados = (
    pd.read_csv("dados/empregados.csv")
    .assign(data = lambda x: pd.to_datetime(x.data))
    .set_index("data")
    )
dados
            empregados_no_varejo
data                            
1995-01-01               13775.2
1995-02-01               13579.5
1995-03-01               13539.2
1995-04-01               13654.1
1995-05-01               13757.5
...                          ...
2019-05-01               15691.6
2019-06-01               15775.5
2019-07-01               15785.9
2019-08-01               15749.5
2019-09-01               15611.3

[297 rows x 1 columns]
# Performa o ajuste sazonal
ajuste = seasonal_decompose(dados['empregados_no_varejo'], model='additive', period=12)
#x13.x13_arima_analysis(endog = dados.empregados_no_varejo, freq = "M")

ajuste.observed
data
1995-01-01    13775.2
1995-02-01    13579.5
1995-03-01    13539.2
1995-04-01    13654.1
1995-05-01    13757.5
               ...   
2019-05-01    15691.6
2019-06-01    15775.5
2019-07-01    15785.9
2019-08-01    15749.5
2019-09-01    15611.3
Name: empregados_no_varejo, Length: 297, dtype: float64
ajuste.seasonal
data
1995-01-01    -86.080304
1995-02-01   -280.313985
1995-03-01   -247.450443
1995-04-01   -185.463796
1995-05-01    -82.955644
                 ...    
2019-05-01    -82.955644
2019-06-01     -6.903832
2019-07-01    -11.089332
2019-08-01    -10.104262
2019-09-01    -97.809297
Name: seasonal, Length: 297, dtype: float64
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

plt.rcParams.update({'figure.figsize': (16,12)})

ajuste.plot().suptitle('Decomposição Aditiva', fontsize=16)
plt.tight_layout(rect=[0, 0.03, 1, 0.95])

plt.show()

# Visualização de dados
(
    dados
    .assign(ajuste = ajuste.observed-ajuste.seasonal)
    .plot(title = "Empregados no Varejo (BSL/US)", xlabel = "", figsize = (10, 5))
    .legend(["Série sazonal", "Série dessazonalizada"])
)

Notas de rodapé

  1. Todo este material foi retirado de diversas postagens e cursos da Análise Macro (www.analisemacro.com.br)↩︎